#include <stdlib.h>
#include <stdio.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
        const typeof( ((type *)0)->member ) *__mptr = (ptr); \
        (type *)( (char *)__mptr - offsetof(type,member) );})


typedef struct list_head {
	struct list_head *next, *prev;
}list_head_t;

typedef struct pci_device_id {
	unsigned int vendor, device; /* Vendor and device ID or PCI_ANY_ID */
	unsigned int subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */
	unsigned int classCode, class_mask; /* (class,subclass,prog-if) triplet */
	unsigned long driver_data; /* Data private to the driver */
}pci_device_id_t;


typedef struct pci_driver {
	struct list_head node;
	char *name;
	pci_device_id_t *id_table; 
}pci_driver_t;

int GetMemFromOffset(int fd, off_t offset, char *buf, int len);

int main(int argv, char* args[]){
	int counter = 0;
	int len;
	off_t offset;
	bool isStart = false;
	char modName[] = "", buf[20];
	list_head_t listHead;
	pci_driver_t pciDriver;
	pci_device_id_t idTable;

	if(argv<2){
		printf("No symbol address specified\n");
		return 1;
	}
	sscanf(args[1], "%x", &offset);

    int fd = open("/dev/kmem", O_RDONLY);
    if(-1 == fd){
        perror("open error");
        return -1;
    };
	if((len = GetMemFromOffset(fd, offset, (char*)(&listHead), sizeof(listHead))) != -1){
		while(listHead.next != NULL && offset != (off_t)listHead.next)
		{
			len = GetMemFromOffset(fd, (off_t)(container_of(listHead.next, pci_driver_t, node)), (char*)(&pciDriver), sizeof(pciDriver));
			if(len != -1){
				memset(buf, 0, 20);
				len = GetMemFromOffset(fd, (off_t)(pciDriver.name), buf, 20);
				if(len != -1)
				{
					buf[19] = '\0';
					printf(" --%s\n", buf);
				}
				len = GetMemFromOffset(fd, (off_t)(pciDriver.id_table), (char*)(&idTable), sizeof(idTable));
				int readCount = 1;

				while(idTable.vendor !=0 && idTable.device !=0){
                                       printf("    %08x  %08x  %08x  %08x  %08x\n", 
                                       idTable.vendor, idTable.device, idTable.subvendor, idTable.subdevice, idTable.classCode);

					if(-1 == GetMemFromOffset(fd, (off_t)pciDriver.id_table + readCount* sizeof	(idTable), (char*)(&idTable), sizeof(idTable)))
					{
                                           break;
                                       }
					readCount++;
				}
			}else{
				break;
			}
			len = GetMemFromOffset(fd, (off_t)(listHead.next), (char*)(&listHead), sizeof(listHead));
		}
	}
	close(fd);
	return 0;
}

int GetMemFromOffset(int fd, off_t offset, char *buf, int len){
	int number = 0;
    if(-1 == lseek(fd, offset, SEEK_SET)){
        //perror("lseek error");
        return -1;
    }else{
        memset(buf, 0, len);
        if(-1 == (number = read(fd, buf, len))){
            //perror("read error");
            return -1;
        }else{
            return number;
        }
    }
}
//get_kernel_syms(table) system calls have not yet been included in any library, which means
//     that they have to be called by the syscall(__NR_function) mechanism.
/*
	int count = 0;
	struct kernel_sym table[2048];
	count = syscall(130, table);
	for(int i=0; i<count; i++){
		printf("  %08x   %s\n", table[i].value, table[i].name);
	}
*/
